- 
          
- 
                Notifications
    You must be signed in to change notification settings 
- Fork 33.6k
async_hooks: move statement #14054
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
async_hooks: move statement #14054
Conversation
| The simplification conflicts with #14051 If you think this simplification is better, could you split up the PR so we can review the commits seperately? | 
| @AndreasMadsen Oops. Hm, I don't have a strong opinion. As you've worked far more with async_hooks, I'd say it's your call. | 
        
          
                lib/async_hooks.js
              
                Outdated
          
        
      There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So... We might want this exposed as the standard way to fail from a callback or Promise.catch... so failing with just a message might be useful... Also we're trying to remove non NodeErrors from the code (even if they are just stack containers).
IMHO restore the weird captureStackTrace case, and please add it to the module.exports = {
Ref: #13839 (comment)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nevermind re #14051 (comment), I'll move it to test/common
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IMHO restore the weird
captureStackTracecase, and please add it to themodule.exports = {
I completely disagree, there is no good reason for stack trace to start in fatalError, it should start where the error occurred. And async_hooks should definitely not export fatalError, as fatalError has nothing to do with async_hooks.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@AndreasMadsen just beat me by a minute 😄
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Only first commit
| 
 A bit off topic but your comment made me think we might at some point want to create long stacks for our exceptions, probably just for  | 
| What are "long stacks"? | 
| 
 I have now confirmed that the issue in #14051 has nothing to do with my changes. So I think I prefer my approach in #14051. This is how it is done in the async_hooks C++, I think it makes sense to use the same approach in async_hooks JS. | 
| 
 async-context stacktraces. But that discussion should be in a separate thread. | 
16f125d    to
    6373ab2      
    Compare
  
    | 
 It should be possible to provoke an error in the previous implementation, by: 
 [refack: fixed typo] | 
| @AndreasMadsen Here's a test for what I believe you're talking about: 'use strict';
const print = process._rawDebug;
const init_calls = { hook1: 0, hook2: 0 };
let hook2_enabled = true;
process.on('exit', () => {
  require('assert').strictEqual(init_calls.hook1, init_calls.hook2);
});
const hook1 = require('async_hooks').createHook({
  init(id, type) {
    init_calls.hook1++;
    print('hook1 init:', id, type);
    if (hook2_enabled) {
      hook2_enabled = false;
      hook2.enable();
      require('crypto').randomBytes(1024, () => {});
    }
  }
});
const hook2 = require('async_hooks').createHook({
  init(id, type) {
    init_calls.hook2++;
    print('hook2 init:', id, type);
  },
});
setImmediate(() => {
  hook1.enable();
  hook2.enable();
  require('fs').open('/dev/null', 'r', (er, fd) => {});
});This shows that init still runs for both  assert.js:60
  throw new errors.AssertionError({
  ^
AssertionError [ERR_ASSERTION]: 2 === 1
    at process.on (/tmp/async-fs-init.js:8:21)
    at emitOne (events.js:115:13)
    at process.emit (events.js:210:7) | 
| @trevnorris there is a comment in the code that says: // The set of callbacks for a hook should be the same regardless of whether
// enable()/disable() are run during their execution.If I understand that comment correctly, it says that  | 
| @AndreasMadsen Here's a more generalized test for discussion: 'use strict';
const { createHook } = require('async_hooks');
const { randomBytes } = require('crypto');
const print = process._rawDebug;
const hooks_list = [];
const start_enabled = process.argv[2] === '--start-enabled';
let cntr = -1;
for (let i = 0; i < 2; i++) {
  hooks_list.push(createHook({
    init(id, type) { print(`hook${i} init`); },
  }));
  if (start_enabled) hooks_list[hooks_list.length - 1].enable();
}
const hook = createHook({
  init(id, type) {
    print('hook init:', id, type);
    if (++cntr < hooks_list.length) {
      if (start_enabled)
        hooks_list[cntr].disable();
      else
        hooks_list[cntr].enable();
      randomBytes(1, () => {});
    }
  }
}).enable();
randomBytes(1, () => {});First running without  Now running with  First thing to address is that I wouldn't expect the output for running with  As we can see from the first results table there's a strange timing issue. Namely I would have expected the output for running without  For simplicity I would interpret that comment to mean the set of callbacks from the entry point of  | 
| First of all: I really struggle to understand what you are arguing for. Are you against this PR? If this is the case, try logging when  without  You will immediately see that when  Assuming you don't think this PR is wrong but something is odd about the output: In the case without  The reason for this is a bug, is the use of a single flag for  On the surface, this might look fine, but what actually happens is that  To put it differently,  The solution as I see it, is to only let the outer  without  with  I don't know why you would expect the number of called hooks to change in the "with  I will create a pull request that depends on this PR and contains the additional fix I'm suggesting. | 
| PR #14143 contains the suggested fix. | 
| @BridgeAR Having spend some time debugging @trevnorris's issue I think this test better highlights the issue this PR solves: 'use strict';
const common = require('../common');
const assert = require('assert');
const async_hooks = require('async_hooks');
const crypto = require('crypto');
const nestedHook = async_hooks.createHook({
  init: common.mustCall()
});
const rootHook = async_hooks.createHook({
  init: common.mustCall(function (id, type) {
    nestedHook.enable();
  }, 2)
}).enable();
crypto.randomBytes(1, common.mustCall(function () {
  crypto.randomBytes(1, common.mustCall());
})); | 
| @AndreasMadsen thanks a lot for the test. As you opened the follow up PR and did pretty much all work here, I could also close this one and you add the test to your PR? | 
| 
 You have my permission to just copy and paste the test case I wrote. If @trevnorris thinks we should land this and #14143 together for it to make sense, then I will just rebase #14143 again. | 
This fixes an error that could occure by nesting async_hooks calls
6373ab2    to
    120f2e2      
    Compare
  
    | Rebased (I changed the commit message a bit) and incorporated. | 
|  | ||
| const common = require('../common'); | ||
| const async_hooks = require('async_hooks'); | ||
| const crypto = require('crypto'); | 
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if you're using crypto you need:
if (!common.hasCrypto)
  common.skip('missing crypto');
AFAICT this should work with fs or stream...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good point, fs should work (for example fs.access(__filename, () => {})).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@AndreasMadsen Thanks for the additional fix. I'm cool to land this along side your other fix.
| landed in 8a83035 | 
This fixes an error that could occure by nesting async_hooks calls PR-URL: #14054 Ref: #13755 (comment) Reviewed-By: Refael Ackermann <[email protected]> Reviewed-By: Trevor Norris <[email protected]> Reviewed-By: James M Snell <[email protected]> Reviewed-By: Andreas Madsen <[email protected]>
In some cases restoreTmpHooks is called too early, this causes active_hooks_array to change during execution of the init hooks. PR-URL: #14143 Ref: #14054 (comment) Reviewed-By: Refael Ackermann <[email protected]> Reviewed-By: Trevor Norris <[email protected]> Reviewed-By: Anna Henningsen <[email protected]>
This fixes an error that could occure by nesting async_hooks calls PR-URL: #14054 Ref: #13755 (comment) Reviewed-By: Refael Ackermann <[email protected]> Reviewed-By: Trevor Norris <[email protected]> Reviewed-By: James M Snell <[email protected]> Reviewed-By: Andreas Madsen <[email protected]>
In some cases restoreTmpHooks is called too early, this causes active_hooks_array to change during execution of the init hooks. PR-URL: #14143 Ref: #14054 (comment) Reviewed-By: Refael Ackermann <[email protected]> Reviewed-By: Trevor Norris <[email protected]> Reviewed-By: Anna Henningsen <[email protected]>
The fatalError function is overloaded to receive either an error or a string. Instead, it would be simpler to just always provide a error.Removed due to a conflict.I
alsomoved therestoreTmpHookscall. It seemed to be more appropriate in the init function. Ref #13755 (comment)Checklist
make -j4 test(UNIX), orvcbuild test(Windows) passesAffected core subsystem(s)
async_hooks